#pragma once
#include "Mesh.h"

class FnVolumeHarmonicParam
{
public:
  FnVolumeHarmonicParam(Mesh& _mesh);
  ~FnVolumeHarmonicParam(void);


  const Mesh& mesh() const        { return  mesh_; }
  Mesh& mesh()                    { return  mesh_; }

  void  set_weight_type(WeightType _type)      { weight_type_ = _type; }
  WeightType weight_type()                     { return  weight_type_; }

  void  print_info();

  void  init();  
  void  calc();
  int   calc_with_taucs();

  void  update_edge_weights();
  void  calc_param_on_wedges();
  void  calc_param_on_sides();

  void  set_cube_corner_vertices(const std::vector<Mesh::VertexHandle>& _corner_vertices, const std::vector<Param>& _corner_params);
  void  get_cube_corner_vertices(std::vector<Mesh::VertexHandle>& _corner_vertices, std::vector<Param>& _corner_params);

  Scalar cot_triangle(const Mesh::HalfedgeHandle _corner) const;
  Scalar cot_tetrahedron(const Mesh::HalfedgeHandle _corner) const;


  Scalar cot_surface_weight(const Mesh::EdgeHandle _eh) const;
  Scalar cot_volume_weight(const Mesh::EdgeHandle _eh) const;

  void  export_linear_system(const std::string& _A_dat, const std::string& _b_dat);

  Mesh::HalfedgeHandle  next_halfedge_handle_on_wedge(const Mesh::HalfedgeHandle _heh) const;
  Mesh::HalfedgeHandle  prev_halfedge_handle_on_wedge(const Mesh::HalfedgeHandle _heh) const;
  Mesh::HalfedgeHandle  find_first_halfedge_handle_on_wedge(const Mesh::HalfedgeHandle _heh) const;

  void  read_face_side_info(const std::string& _filename);

  void  debug_output_params();
  void  save(const std::string& _filename) const;

  void  debug_save_boundary(const std::string& _filename);

  void  debug_param_flipping_test() const;

  Mesh::Scalar  calc_cell_signed_distance_param_volume(const Mesh::CellHandle _ch) const;


private:
  Mesh&       mesh_;
  WeightType  weight_type_;
  std::vector<Mesh::VertexHandle>     cube_corner_vertices_;
  std::vector<Mesh::VertexHandle>     square_corner_vertices_[6];   // for 6-sides


  OpenVolMesh::VPropHandleT<int>          vprop_lns_idx_;     // indices in the linear system

  OpenVolMesh::VPropHandleT<Param>        vprop_param_;
  OpenVolMesh::EPropHandleT<Scalar>       eprop_volume_w_;
  OpenVolMesh::EPropHandleT<Scalar>       eprop_surface_w_;  
  OpenVolMesh::FPropHandleT<int>          fprop_side_idx_;
  OpenVolMesh::VPropHandleT<VertexType>   vprop_type_;
  OpenVolMesh::EPropHandleT<EdgeType>     eprop_type_;

  int nSides;

};